// Burner Game Input
#include "burner.h"
// Key codes
#include "inp_keys.h"

// Player Default Controls
int playerDefaultControls[4] = {0, 1, 2, 3};
wchar_t playerDefaultIni[4][MAX_PATH] = { L"", L"", L"", L"" };

// Mapping of PC inputs to game inputs
GameInp* gameInput = NULL;
unsigned int gameInpCount = 0;
unsigned int macroCount = 0;
unsigned int maxMacro = 0;

int analogSpeed = 0x0100;
int fireButtons = 0;

bool sfLayout = false;
bool leftAltkeyMapped = false;

const int maxMacroPerPlayer = 10;

// ---------------------------------------------------------------------------

// Check if the left alt (menu) key is mapped
void gameInpCheckLeftAlt()
{
	GameInp* pgi;
	unsigned int i;

	leftAltkeyMapped = false;

	for (i = 0, pgi = gameInput; i < (gameInpCount + macroCount); i++, pgi++) {

		if (leftAltkeyMapped) {
			break;
		}

		switch (pgi->nInput) {
			case GIT_SWITCH:
				if (pgi->Input.Switch.nCode == FBK_LALT) {
					leftAltkeyMapped = true;
				}
				break;
			case GIT_MACRO_AUTO:
			case GIT_MACRO_CUSTOM:
				if (pgi->Macro.nMode) {
					if (pgi->Macro.Switch.nCode == FBK_LALT) {
						leftAltkeyMapped = true;
					}
				}
				break;

			default:
				continue;
		}
	}
}

// Check if the sytem mouse is mapped and set the cooperative level apropriately
void gameInpCheckMouse()
{
	bool mouseMapped = false;
	GameInp* pgi;
	unsigned int i;

	for (i = 0, pgi = gameInput; i < (gameInpCount + macroCount); i++, pgi++) {

		if (mouseMapped) {
			break;
		}

		switch (pgi->nInput) {
			case GIT_SWITCH:
				if ((pgi->Input.Switch.nCode & 0xFF00) == 0x8000) {
					mouseMapped = true;
				}
				break;
			case GIT_MOUSEAXIS:
				if (pgi->Input.MouseAxis.nMouse == 0) {
					mouseMapped = true;
				}
				break;
			case GIT_MACRO_AUTO:
			case GIT_MACRO_CUSTOM:
				if (pgi->Macro.nMode) {
					if ((pgi->Macro.Switch.nCode & 0xFF00) == 0x8000) {
						mouseMapped = true;
					}
				}
				break;

			default:
				continue;
		}
	}

	if (driverOkay) {
		if (!runPause) {
			inputInterface.setCooperativeLevel(mouseMapped, !alwaysProcessKey);
		} else {
			inputInterface.setCooperativeLevel(false, !alwaysProcessKey);
		}
	} else {
		inputInterface.setCooperativeLevel(false, false);
	}
}

// ---------------------------------------------------------------------------

int gameInpBlank(int dipSwitch)
{
	unsigned int i = 0;
	GameInp* pgi = NULL;

	// Reset all inputs to undefined (even dip switches, if dipSwitch==1)
	if (gameInput == NULL) {
		return 1;
	}

	// Get the targets in the library for the Input Values
	struct BurnInputInfo bii;

	for (i = 0, pgi = gameInput; i < gameInpCount; i++, pgi++) {
		memset(&bii, 0, sizeof(bii));
		BurnDrvGetInputInfo(&bii, i);
		if (dipSwitch == 0 && (bii.nType & BIT_GROUP_CONSTANT)) {		// Don't blank the dip switches
			continue;
		}

		memset(pgi, 0, sizeof(*pgi));									// Clear input

		pgi->nType = bii.nType;											// store input type
		pgi->Input.pVal = bii.pVal;										// store input pointer to value

		if (bii.nType & BIT_GROUP_CONSTANT) {							// Further initialisation for constants/DIPs
			pgi->nInput = GIT_CONSTANT;
			pgi->Input.Constant.nConst = *bii.pVal;
		}
	}

	for (i = 0; i < macroCount; i++, pgi++) {
		pgi->Macro.nMode = 0;
		if (pgi->nInput == GIT_MACRO_CUSTOM) {
			pgi->nInput = 0;
		}
	}

	leftAltkeyMapped = false;

	return 0;
}

static void gameInpInitMacros()
{
	GameInp* pgi;
	BurnInputInfo bii;

	int nPunchx3[4] = {0, 0, 0, 0};
	int nPunchInputs[4][3];
	int nKickx3[4] = {0, 0, 0, 0};
	int nKickInputs[4][3];

	int nNeogeoButtons[4][4];
	int nPgmButtons[4][4];

	sfLayout = false;
	macroCount = 0;

	fireButtons = 0;

	for (unsigned int i = 0; i < gameInpCount; i++) {
		bii.szName = NULL;
		BurnDrvGetInputInfo(&bii, i);
		if (bii.szName == NULL) {
			bii.szName = "";
		}
		if (bii.szName[0] == 'P' && bii.szName[1] >= '1' && bii.szName[1] <= '4') {
			int nPlayer = bii.szName[1] - '1';

			if (nPlayer == 0) {
				if (strncmp(" fire", bii.szInfo + 2, 5) == 0) {
					fireButtons++;
				}
			}

			if (_stricmp(" Weak Punch", bii.szName + 2) == 0) {
				nPunchx3[nPlayer] |= 1;
				nPunchInputs[nPlayer][0] = i;
			}
			if (_stricmp(" Medium Punch", bii.szName + 2) == 0) {
				nPunchx3[nPlayer] |= 2;
				nPunchInputs[nPlayer][1] = i;
			}
			if (_stricmp(" Strong Punch", bii.szName + 2) == 0) {
				nPunchx3[nPlayer] |= 4;
				nPunchInputs[nPlayer][2] = i;
			}
			if (_stricmp(" Weak Kick", bii.szName + 2) == 0) {
				nKickx3[nPlayer] |= 1;
				nKickInputs[nPlayer][0] = i;
			}
			if (_stricmp(" Medium Kick", bii.szName + 2) == 0) {
				nKickx3[nPlayer] |= 2;
				nKickInputs[nPlayer][1] = i;
			}
			if (_stricmp(" Strong Kick", bii.szName + 2) == 0) {
				nKickx3[nPlayer] |= 4;
				nKickInputs[nPlayer][2] = i;
			}
			if (_stricmp(" Attack", bii.szName + 2) == 0) {
				nPunchx3[nPlayer] |= 1;
				nPunchInputs[nPlayer][0] = i;
			}
			if (_stricmp(" Jump", bii.szName + 2) == 0) {
				nPunchx3[nPlayer] |= 2;
				nPunchInputs[nPlayer][1] = i;
			}

			if (_stricmp(" Button A", bii.szName + 2) == 0) {
				nNeogeoButtons[nPlayer][0] = i;
			}
			if (_stricmp(" Button B", bii.szName + 2) == 0) {
				nNeogeoButtons[nPlayer][1] = i;
			}
			if (_stricmp(" Button C", bii.szName + 2) == 0) {
				nNeogeoButtons[nPlayer][2] = i;
			}
			if (_stricmp(" Button D", bii.szName + 2) == 0) {
				nNeogeoButtons[nPlayer][3] = i;
			}

			if (_stricmp(" Button 1", bii.szName + 2) == 0) {
				nPgmButtons[nPlayer][0] = i;
			}
			if (_stricmp(" Button 2", bii.szName + 2) == 0) {
				nPgmButtons[nPlayer][1] = i;
			}
			if (_stricmp(" Button 3", bii.szName + 2) == 0) {
				nPgmButtons[nPlayer][2] = i;
			}
			if (_stricmp(" Button 4", bii.szName + 2) == 0) {
				nPgmButtons[nPlayer][3] = i;
			}
		}
	}

	pgi = gameInput + gameInpCount;

	for (int nPlayer = 0; nPlayer < maxPlayers; nPlayer++) {
		if (nPunchx3[nPlayer] == 7) {		// Create a 3x punch macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i 3x Punch", nPlayer + 1);
			for (int j = 0; j < 3; j++) {
				BurnDrvGetInputInfo(&bii, nPunchInputs[nPlayer][j]);
				pgi->Macro.pVal[j] = bii.pVal;
				pgi->Macro.nVal[j] = 1;
			}

			macroCount++;
			pgi++;
		}

		if (nKickx3[nPlayer] == 7) {		// Create a 3x kick macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i 3x Kick", nPlayer + 1);
			for (int j = 0; j < 3; j++) {
				BurnDrvGetInputInfo(&bii, nKickInputs[nPlayer][j]);
				pgi->Macro.pVal[j] = bii.pVal;
				pgi->Macro.nVal[j] = 1;
			}

			macroCount++;
			pgi++;
		}

		if (nPunchx3[nPlayer] == 3) {		// Create a special macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Special", nPlayer + 1);
			for (int j = 0; j < 2; j++) {
				BurnDrvGetInputInfo(&bii, nPunchInputs[nPlayer][j]);
				pgi->Macro.pVal[j] = bii.pVal;
				pgi->Macro.nVal[j] = 1;
			}

			macroCount++;
			pgi++;
		}

		if (fireButtons == 4 && (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_SNK_NEOGEO) {

			// A + B macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button AB", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][0]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][1]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// A + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button AC", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][0]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][2]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// A + D macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button AD", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][0]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][3]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// B + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button BC", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][1]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][2]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// B + D macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button BD", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][1]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][3]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// C + D macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button CD", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][2]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][3]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// A + B + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button ABC", nPlayer + 1);
			for (int i = 0; i < 3; i++) {
				BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][i]);
				pgi->Macro.pVal[i] = bii.pVal;
				pgi->Macro.nVal[i] = 1;
			}

			macroCount++;
			pgi++;

			// B + C + D macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button BCD", nPlayer + 1);
			for (int i = 0; i < 3; i++) {
				BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][i+1]);
				pgi->Macro.pVal[i] = bii.pVal;
				pgi->Macro.nVal[i] = 1;
			}

			macroCount++;
			pgi++;

			// A + B + C + D macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button ABCD", nPlayer + 1);
			for (int i = 0; i < 4; i++) {
				BurnDrvGetInputInfo(&bii, nNeogeoButtons[nPlayer][i]);
				pgi->Macro.pVal[i] = bii.pVal;
				pgi->Macro.nVal[i] = 1;
			}

			macroCount++;
			pgi++;
		}

		if (fireButtons == 4 && (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_IGS_PGM) {

			// A + B macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button AB", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][0]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][1]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// B + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button BC", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][1]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][2]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// A + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button AC", nPlayer + 1);
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][0]);
			pgi->Macro.pVal[0] = bii.pVal;
			pgi->Macro.nVal[0] = 1;
			BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][2]);
			pgi->Macro.pVal[1] = bii.pVal;
			pgi->Macro.nVal[1] = 1;

			macroCount++;
			pgi++;

			// A + B + C macro
			pgi->nInput = GIT_MACRO_AUTO;
			pgi->nType = BIT_DIGITAL;
			pgi->Macro.nMode = 0;

			sprintf(pgi->Macro.szName, "P%i Button ABC", nPlayer + 1);
			for (int i = 0; i < 3; i++) {
				BurnDrvGetInputInfo(&bii, nPgmButtons[nPlayer][i]);
				pgi->Macro.pVal[i] = bii.pVal;
				pgi->Macro.nVal[i] = 1;
			}

			macroCount++;
			pgi++;
		}
	}

	if ((nPunchx3[0] == 7) && (nKickx3[0] == 7)) {
		sfLayout = true;
	}
	if (fireButtons >= 5 && (BurnDrvGetHardwareCode() & HARDWARE_PUBLIC_MASK) == HARDWARE_CAPCOM_CPS2) {
		sfLayout = true;
	}
}

int gameInpInit()
{
	// Count the number of inputs
	gameInpCount = 0;
	macroCount = 0;
	maxMacro = maxPlayers * maxMacroPerPlayer;

	for (unsigned int i = 0; i < 0x1000; i++) {
		if (BurnDrvGetInputInfo(NULL, i)) {			// end of input list
			gameInpCount = i;
			break;
		}
	}

	// Allocate space for all the inputs
	int size = (gameInpCount + maxMacro) * sizeof(GameInp);
	gameInput = new GameInp[size];
	if (gameInput == NULL) {
		return 1;
	}
	memset(gameInput, 0, size);

	gameInpBlank(1);

	inpDIPSWResetDIPs();

	gameInpInitMacros();

	analogSpeed = 0x0100;

	return 0;
}

int gameInpExit()
{
	delete [] gameInput;
	gameInput = NULL;

	gameInpCount = 0;
	macroCount = 0;

	fireButtons = 0;

	sfLayout = false;
	leftAltkeyMapped = false;

	return 0;
}

// ---------------------------------------------------------------------------
// Convert a string from a config file to an input

static wchar_t* sliderInfo(GameInp* pgi, wchar_t* s)
{
	pgi->Input.Slider.nSliderSpeed = 0x700;				// defaults
	pgi->Input.Slider.nSliderCenter = 0;
	pgi->Input.Slider.nSliderValue = 0x8000;

	wchar_t* szRet = labelCheck(s, L"speed");
	s = szRet;
	if (s == NULL) {
		return s;
	}
	pgi->Input.Slider.nSliderSpeed = (short)wcstol(s, &szRet, 0);
	s = szRet;
	if (s==NULL) {
		return s;
	}
	szRet = labelCheck(s, L"center");
	s = szRet;
	if (s == NULL) {
		return s;
	}
	pgi->Input.Slider.nSliderCenter = (short)wcstol(s, &szRet, 0);
	s = szRet;
	if (s == NULL) {
		return s;
	}

	return szRet;
}

static int stringToJoyAxis(GameInp* pgi, wchar_t* s)
{
	wchar_t* szRet = s;

	pgi->Input.JoyAxis.nJoy = (unsigned char)wcstol(s, &szRet, 0);
	if (szRet == NULL) {
		return 1;
	}
	s = szRet;
	pgi->Input.JoyAxis.nAxis = (unsigned char)wcstol(s, &szRet, 0);
	if (szRet == NULL) {
		return 1;
	}

	return 0;
}

static int stringToMouseAxis(GameInp* pgi, wchar_t* s)
{
	wchar_t* szRet = s;

	pgi->Input.MouseAxis.nAxis = (unsigned char)wcstol(s, &szRet, 0);
	if (szRet == NULL) {
		return 1;
	}

	return 0;
}

static int stringToMacro(GameInp* pgi, wchar_t* s)
{
	wchar_t* szRet = labelCheck(s, L"switch");
	if (szRet) {
		s = szRet;
		pgi->Macro.nMode = 0x01;
		pgi->Macro.Switch.nCode = (unsigned short)wcstol(s, &szRet, 0);
		return 0;
	}

	return 1;
}

static int stringToInp(GameInp* pgi, wchar_t* s)
{
	SKIP_WS(s);											// skip whitespace
	wchar_t* szRet = labelCheck(s, L"undefined");
	if (szRet) {
		pgi->nInput = 0;
		return 0;
	}

	szRet = labelCheck(s, L"constant");
	if (szRet) {
		pgi->nInput = GIT_CONSTANT;
		s = szRet;
		pgi->Input.Constant.nConst=(unsigned char)wcstol(s, &szRet, 0);
		*(pgi->Input.pVal) = pgi->Input.Constant.nConst;
		return 0;
	}

	szRet = labelCheck(s, L"switch");
	if (szRet) {
		pgi->nInput = GIT_SWITCH;
		s = szRet;
		pgi->Input.Switch.nCode = (unsigned short)wcstol(s, &szRet, 0);
		return 0;
	}

	// Analog using mouse axis:
	szRet = labelCheck(s, L"mouseaxis");
	if (szRet) {
		pgi->nInput = GIT_MOUSEAXIS;
		return stringToMouseAxis(pgi, szRet);
	}
	// Analog using joystick axis:
	szRet = labelCheck(s, L"joyaxis-neg");
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_NEG;
		return stringToJoyAxis(pgi, szRet);
	}
	szRet = labelCheck(s, L"joyaxis-pos");
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_POS;
		return stringToJoyAxis(pgi, szRet);
	}
	szRet = labelCheck(s, L"joyaxis");
	if (szRet) {
		pgi->nInput = GIT_JOYAXIS_FULL;
		return stringToJoyAxis(pgi, szRet);
	}

	// Analog using keyboard slider
	szRet = labelCheck(s, L"slider");
	if (szRet) {
		s = szRet;
		pgi->nInput = GIT_KEYSLIDER;
		pgi->Input.Slider.SliderAxis.nSlider[0] = 0;	// defaults
		pgi->Input.Slider.SliderAxis.nSlider[1] = 0;	//

		pgi->Input.Slider.SliderAxis.nSlider[0] = (unsigned short)wcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		pgi->Input.Slider.SliderAxis.nSlider[1] = (unsigned short)wcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		szRet = sliderInfo(pgi, s);
		s = szRet;
		if (s == NULL) {								// Get remaining slider info
			return 1;
		}
		return 0;
	}

	// Analog using joystick slider
	szRet = labelCheck(s, L"joyslider");
	if (szRet) {
		s = szRet;
		pgi->nInput = GIT_JOYSLIDER;
		pgi->Input.Slider.JoyAxis.nJoy = 0;				// defaults
		pgi->Input.Slider.JoyAxis.nAxis = 0;			//

		pgi->Input.Slider.JoyAxis.nJoy = (unsigned char)wcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		pgi->Input.Slider.JoyAxis.nAxis = (unsigned char)wcstol(s, &szRet, 0);
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		szRet = sliderInfo(pgi, s);						// Get remaining slider info
		s = szRet;
		if (s == NULL) {
			return 1;
		}
		return 0;
	}

	return 1;
}

// ---------------------------------------------------------------------------
// Convert an input to a string for config files

static wchar_t* inpToString(GameInp* pgi)
{
	static wchar_t szString[80];

	if (pgi->nInput == 0) {
		return L"undefined";
	}
	if (pgi->nInput == GIT_CONSTANT) {
		swprintf(szString, L"constant 0x%.2X", pgi->Input.Constant.nConst);
		return szString;
	}
	if (pgi->nInput == GIT_SWITCH) {
		swprintf(szString, L"switch 0x%.2X", pgi->Input.Switch.nCode);
		return szString;
	}
	if (pgi->nInput == GIT_KEYSLIDER) {
		swprintf(szString, L"slider 0x%.2x 0x%.2x speed 0x%x center %d", pgi->Input.Slider.SliderAxis.nSlider[0], pgi->Input.Slider.SliderAxis.nSlider[1], pgi->Input.Slider.nSliderSpeed, pgi->Input.Slider.nSliderCenter);
		return szString;
	}
	if (pgi->nInput == GIT_JOYSLIDER) {
		swprintf(szString, L"joyslider %d %d speed 0x%x center %d", pgi->Input.Slider.JoyAxis.nJoy, pgi->Input.Slider.JoyAxis.nAxis, pgi->Input.Slider.nSliderSpeed, pgi->Input.Slider.nSliderCenter);
		return szString;
	}
	if (pgi->nInput == GIT_MOUSEAXIS) {
		swprintf(szString, L"mouseaxis %d", pgi->Input.MouseAxis.nAxis);
		return szString;
	}
	if (pgi->nInput == GIT_JOYAXIS_FULL) {
		swprintf(szString, L"joyaxis %d %d", pgi->Input.JoyAxis.nJoy, pgi->Input.JoyAxis.nAxis);
		return szString;
	}
	if (pgi->nInput == GIT_JOYAXIS_NEG) {
		swprintf(szString, L"joyaxis-neg %d %d", pgi->Input.JoyAxis.nJoy, pgi->Input.JoyAxis.nAxis);
		return szString;
	}
	if (pgi->nInput == GIT_JOYAXIS_POS) {
		swprintf(szString, L"joyaxis-pos %d %d", pgi->Input.JoyAxis.nJoy, pgi->Input.JoyAxis.nAxis);
		return szString;
	}

	return L"unknown";
}

static wchar_t* inpMacroToString(GameInp* pgi)
{
	static wchar_t szString[MAX_PATH];

	if (pgi->nInput == GIT_MACRO_AUTO) {
		if (pgi->Macro.nMode) {
			swprintf(szString, L"switch 0x%.2X", pgi->Macro.Switch.nCode);
			return szString;
		}
	}

	if (pgi->nInput == GIT_MACRO_CUSTOM) {
		struct BurnInputInfo bii;

		if (pgi->Macro.nMode) {
			swprintf(szString, L"switch 0x%.2X", pgi->Macro.Switch.nCode);
		} else {
			swprintf(szString, L"undefined");
		}

		for (int i = 0; i < 4; i++) {
			if (pgi->Macro.pVal[i]) {
				BurnDrvGetInputInfo(&bii, pgi->Macro.nInput[i]);
				swprintf(szString + wcslen(szString), L" \"%hs\" 0x%02X", bii.szName, pgi->Macro.nVal[i]);
			}
		}

		return szString;
	}

	return L"undefined";
}

// ---------------------------------------------------------------------------
// Generate a user-friendly name for a control (PC-side)

static struct { int nCode; wchar_t* szName; } KeyNames[] = {

#define FBK_DEFNAME(k) k, L#k

	{ FBK_ESCAPE,				L"ESCAPE" },
	{ FBK_1,					L"1" },
	{ FBK_2,					L"2" },
	{ FBK_3,					L"3" },
	{ FBK_4,					L"4" },
	{ FBK_5,					L"5" },
	{ FBK_6,					L"6" },
	{ FBK_7,					L"7" },
	{ FBK_8,					L"8" },
	{ FBK_9,					L"9" },
	{ FBK_0,					L"0" },
	{ FBK_MINUS,				L"MINUS" },
	{ FBK_EQUALS,				L"EQUALS" },
	{ FBK_BACK,					L"BACKSPACE" },
	{ FBK_TAB,					L"TAB" },
	{ FBK_Q,					L"Q" },
	{ FBK_W,					L"W" },
	{ FBK_E,					L"E" },
	{ FBK_R,					L"R" },
	{ FBK_T,					L"T" },
	{ FBK_Y,					L"Y" },
	{ FBK_U,					L"U" },
	{ FBK_I,					L"I" },
	{ FBK_O,					L"O" },
	{ FBK_P,					L"P" },
	{ FBK_LBRACKET,				L"OPENING BRACKET" },
	{ FBK_RBRACKET,				L"CLOSING BRACKET" },
	{ FBK_RETURN,				L"ENTER" },
	{ FBK_LCONTROL,				L"LEFT CONTROL" },
	{ FBK_A,					L"A" },
	{ FBK_S,					L"S" },
	{ FBK_D,					L"D" },
	{ FBK_F,					L"F" },
	{ FBK_G,					L"G" },
	{ FBK_H,					L"H" },
	{ FBK_J,					L"J" },
	{ FBK_K,					L"K" },
	{ FBK_L,					L"L" },
	{ FBK_SEMICOLON,			L"SEMICOLON" },
	{ FBK_APOSTROPHE,			L"APOSTROPHE" },
	{ FBK_GRAVE,				L"ACCENT GRAVE" },
	{ FBK_LSHIFT,				L"LEFT SHIFT" },
	{ FBK_BACKSLASH,			L"BACKSLASH" },
	{ FBK_Z,					L"Z" },
	{ FBK_X,					L"X" },
	{ FBK_C,					L"C" },
	{ FBK_V,					L"V" },
	{ FBK_B,					L"B" },
	{ FBK_N,					L"N" },
	{ FBK_M,					L"M" },
	{ FBK_COMMA,				L"COMMA" },
	{ FBK_PERIOD,				L"PERIOD" },
	{ FBK_SLASH,				L"SLASH" },
	{ FBK_RSHIFT,				L"RIGHT SHIFT" },
	{ FBK_MULTIPLY,				L"NUMPAD MULTIPLY" },
	{ FBK_LALT,					L"LEFT MENU" },
	{ FBK_SPACE,				L"SPACE" },
	{ FBK_CAPITAL,				L"CAPSLOCK" },
	{ FBK_F1,					L"F1" },
	{ FBK_F2,					L"F2" },
	{ FBK_F3,					L"F3" },
	{ FBK_F4,					L"F4" },
	{ FBK_F5,					L"F5" },
	{ FBK_F6,					L"F6" },
	{ FBK_F7,					L"F7" },
	{ FBK_F8,					L"F8" },
	{ FBK_F9,					L"F9" },
	{ FBK_F10,					L"F10" },
	{ FBK_NUMLOCK,				L"NUMLOCK" },
	{ FBK_SCROLL,				L"SCROLLLOCK" },
	{ FBK_NUMPAD7,				L"NUMPAD 7" },
	{ FBK_NUMPAD8,				L"NUMPAD 8" },
	{ FBK_NUMPAD9,				L"NUMPAD 9" },
	{ FBK_SUBTRACT,				L"NUMPAD SUBTRACT" },
	{ FBK_NUMPAD4,				L"NUMPAD 4" },
	{ FBK_NUMPAD5,				L"NUMPAD 5" },
	{ FBK_NUMPAD6,				L"NUMPAD 6" },
	{ FBK_ADD,					L"NUMPAD ADD" },
	{ FBK_NUMPAD1,				L"NUMPAD 1" },
	{ FBK_NUMPAD2,				L"NUMPAD 2" },
	{ FBK_NUMPAD3,				L"NUMPAD 3" },
	{ FBK_NUMPAD0,				L"NUMPAD 0" },
	{ FBK_DECIMAL,				L"NUMPAD DECIMAL POINT" },
	{ FBK_DEFNAME(FBK_OEM_102) },
	{ FBK_F11,					L"F11" },
	{ FBK_F12,					L"F12" },
	{ FBK_F13,					L"F13" },
	{ FBK_F14,					L"F14" },
	{ FBK_F15,					L"F15" },
	{ FBK_DEFNAME(FBK_KANA) },
	{ FBK_DEFNAME(FBK_ABNT_C1) },
	{ FBK_DEFNAME(FBK_CONVERT) },
	{ FBK_DEFNAME(FBK_NOCONVERT) },
	{ FBK_DEFNAME(FBK_YEN) },
	{ FBK_DEFNAME(FBK_ABNT_C2) },
	{ FBK_NUMPADEQUALS,			L"NUMPAD EQUALS" },
	{ FBK_DEFNAME(FBK_PREVTRACK) },
	{ FBK_DEFNAME(FBK_AT) },
	{ FBK_COLON,				L"COLON" },
	{ FBK_UNDERLINE,			L"UNDERSCORE" },
	{ FBK_DEFNAME(FBK_KANJI) },
	{ FBK_DEFNAME(FBK_STOP) },
	{ FBK_DEFNAME(FBK_AX) },
	{ FBK_DEFNAME(FBK_UNLABELED) },
	{ FBK_DEFNAME(FBK_NEXTTRACK) },
	{ FBK_NUMPADENTER,			L"NUMPAD ENTER" },
	{ FBK_RCONTROL,				L"RIGHT CONTROL" },
	{ FBK_DEFNAME(FBK_MUTE) },
	{ FBK_DEFNAME(FBK_CALCULATOR) },
	{ FBK_DEFNAME(FBK_PLAYPAUSE) },
	{ FBK_DEFNAME(FBK_MEDIASTOP) },
	{ FBK_DEFNAME(FBK_VOLUMEDOWN) },
	{ FBK_DEFNAME(FBK_VOLUMEUP) },
	{ FBK_DEFNAME(FBK_WEBHOME) },
	{ FBK_DEFNAME(FBK_NUMPADCOMMA) },
	{ FBK_DIVIDE,				L"NUMPAD DIVIDE" },
	{ FBK_SYSRQ,				L"PRINTSCREEN" },
	{ FBK_RALT,					L"RIGHT MENU" },
	{ FBK_PAUSE,				L"PAUSE" },
	{ FBK_HOME,					L"HOME" },
	{ FBK_UPARROW,				L"ARROW UP" },
	{ FBK_PRIOR,				L"PAGE UP" },
	{ FBK_LEFTARROW,			L"ARROW LEFT" },
	{ FBK_RIGHTARROW,			L"ARROW RIGHT" },
	{ FBK_END,					L"END" },
	{ FBK_DOWNARROW,			L"ARROW DOWN" },
	{ FBK_NEXT,					L"PAGE DOWN" },
	{ FBK_INSERT,				L"INSERT" },
	{ FBK_DELETE,				L"DELETE" },
	{ FBK_LWIN,					L"LEFT WINDOWS" },
	{ FBK_RWIN,					L"RIGHT WINDOWS" },
	{ FBK_DEFNAME(FBK_APPS) },
	{ FBK_DEFNAME(FBK_POWER) },
	{ FBK_DEFNAME(FBK_SLEEP) },
	{ FBK_DEFNAME(FBK_WAKE) },
	{ FBK_DEFNAME(FBK_WEBSEARCH) },
	{ FBK_DEFNAME(FBK_WEBFAVORITES) },
	{ FBK_DEFNAME(FBK_WEBREFRESH) },
	{ FBK_DEFNAME(FBK_WEBSTOP) },
	{ FBK_DEFNAME(FBK_WEBFORWARD) },
	{ FBK_DEFNAME(FBK_WEBBACK) },
	{ FBK_DEFNAME(FBK_MYCOMPUTER) },
	{ FBK_DEFNAME(FBK_MAIL) },
	{ FBK_DEFNAME(FBK_MEDIASELECT) },

#undef FBK_DEFNAME

	{ 0,				NULL }
};

wchar_t* inputCodeDesc(int c)
{
	static wchar_t szString[64];
	wchar_t* szName = L"";

	// Mouse
	if (c >= 0x8000) {
		int nMouse = (c >> 8) & 0x3F;
		int nCode = c & 0xFF;
		if (nCode >= 0x80) {
			swprintf(szString, L"Mouse %d Button %d", nMouse, nCode & 0x7F);
			return szString;
		}
		if (nCode < 0x06) {
			wchar_t szAxis[3][3] = { L"X", L"Y", L"Z" };
			wchar_t szDir[6][16] = { L"negative", L"positive", L"Left", L"Right", L"Up", L"Down" };
			if (nCode < 4) {
				swprintf(szString, L"Mouse %d %s (%s %s)", nMouse, szDir[nCode + 2], szAxis[nCode >> 1], szDir[nCode & 1]);
			} else {
				swprintf(szString, L"Mouse %d %s %s", nMouse, szAxis[nCode >> 1], szDir[nCode & 1]);
			}
			return szString;
		}
	}

	// Joystick
	if (c >= 0x4000 && c < 0x8000) {
		int nJoy = (c >> 8) & 0x3F;
		int nCode = c & 0xFF;
		if (nCode >= 0x80) {
			swprintf(szString, L"Joy %d Button %d", nJoy, nCode & 0x7F);
			return szString;
		}
		if (nCode < 0x10) {
			wchar_t szAxis[8][3] = { L"X", L"Y", L"Z", L"rX", L"rY", L"rZ", L"s0", L"s1" };
			wchar_t szDir[6][16] = { L"negative", L"positive", L"Left", L"Right", L"Up", L"Down" };
			if (nCode < 4) {
				swprintf(szString, L"Joy %d %s (%s %s)", nJoy, szDir[nCode + 2], szAxis[nCode >> 1], szDir[nCode & 1]);
			} else {
				swprintf(szString, L"Joy %d %s %s", nJoy, szAxis[nCode >> 1], szDir[nCode & 1]);
			}
			return szString;
		}
		if (nCode < 0x20) {
			wchar_t szDir[4][16] = { L"Left", L"Right", L"Up", L"Down" };
			swprintf(szString, L"Joy %d POV-hat %d %s", nJoy, (nCode & 0x0F) >> 2, szDir[nCode & 3]);
			return szString;
		}
	}

	for (int i = 0; KeyNames[i].nCode; i++) {
		if (c == KeyNames[i].nCode) {
			if (KeyNames[i].szName) {
				szName = KeyNames[i].szName;
			}
			break;
		}
	}

	if (szName[0]) {
		swprintf(szString, L"%s", szName);
	} else {
		swprintf(szString, L"code 0x%.2X", c);
	}

	return szString;
}

wchar_t* inpToDesc(GameInp* pgi)
{
	static wchar_t szInputName[64] = L"";

	if (pgi->nInput == 0) {
		return L"";
	}
	if (pgi->nInput == GIT_CONSTANT) {
		if (pgi->nType & BIT_GROUP_CONSTANT) {
			for (int i = 0; i < 8; i++) {
				szInputName[7 - i] = pgi->Input.Constant.nConst & (1 << i) ? '1' : '0';
			}
			szInputName[8] = 0;

			return szInputName;
		}

		if (pgi->Input.Constant.nConst == 0) {
			return L"-";
		}
	}
	if (pgi->nInput == GIT_SWITCH) {
		return inputCodeDesc(pgi->Input.Switch.nCode);
	}
	if (pgi->nInput == GIT_MOUSEAXIS) {
		wchar_t nAxis = '?';
		switch (pgi->Input.MouseAxis.nAxis) {
			case 0:
				nAxis = 'X';
				break;
			case 1:
				nAxis = 'Y';
				break;
			case 2:
				nAxis = 'Z';
				break;
		}
		swprintf(szInputName, L"Mouse %i %c axis", pgi->Input.MouseAxis.nMouse, nAxis);
		return szInputName;
	}
	if (pgi->nInput & GIT_GROUP_JOYSTICK) {
		wchar_t szAxis[8][3] = { L"X", L"Y", L"Z", L"rX", L"rY", L"rZ", L"s0", L"s1" };
		wchar_t szRange[4][16] = { L"unknown", L"full", L"negative", L"positive" };
		int nRange = 0;
		switch (pgi->nInput) {
			case GIT_JOYAXIS_FULL:
				nRange = 1;
				break;
			case GIT_JOYAXIS_NEG:
				nRange = 2;
				break;
			case GIT_JOYAXIS_POS:
				nRange = 3;
				break;
		}

		swprintf(szInputName, L"Joy %d %s axis (%s range)", pgi->Input.JoyAxis.nJoy, szAxis[pgi->Input.JoyAxis.nAxis], szRange[nRange]);
		return szInputName;
	}

	return inpToString(pgi); // Just do the rest as they are in the config file
}

wchar_t* inpMacroToDesc(GameInp* pgi)
{
	if (pgi->nInput & GIT_GROUP_MACRO) {
		if (pgi->Macro.nMode) {
			return inputCodeDesc(pgi->Macro.Switch.nCode);
		}
	}

	return L"";
}

// ---------------------------------------------------------------------------

// Find the input number by info
static unsigned int InputInfoToNum(wchar_t* szName)
{
	struct BurnInputInfo bii;
	for (unsigned int i = 0; i < gameInpCount; i++) {
		BurnDrvGetInputInfo(&bii, i);
		if (bii.pVal == NULL) {
			continue;
		}

		if (_wcsicmp(szName, AtoW(bii.szInfo)) == 0) {
			return i;
		}
	}
	return ~0U;
}

// Find the input number by name
static unsigned int InputNameToNum(wchar_t* szName)
{
	struct BurnInputInfo bii;
	for (unsigned int i = 0; i < gameInpCount; i++) {
		BurnDrvGetInputInfo(&bii, i);
		if (bii.pVal == NULL) {
			continue;
		}

		if (_wcsicmp(szName, AtoW(bii.szName)) == 0) {
			return i;
		}
	}
	return ~0U;
}

// Get the input name by number
static wchar_t* InputNumToName(unsigned int i)
{
	struct BurnInputInfo bii;
	bii.szName = NULL;
	BurnDrvGetInputInfo(&bii, i);
	if (bii.szName == NULL) {
		return L"unknown";
	}
	return AtoW(bii.szName);
}

// Get the input info by number, added by regret
static wchar_t* InputNumToInfo(unsigned int i)
{
	struct BurnInputInfo bii;
	bii.szName = NULL;
	BurnDrvGetInputInfo(&bii, i);
	if (bii.szInfo == NULL) {
		return L"unknown";
	}
	return AtoW(bii.szInfo);
}

static unsigned int macroNameToNum(wchar_t* szName)
{
	GameInp* pgi = gameInput + gameInpCount;
	for (unsigned int i = 0; i < macroCount; i++, pgi++) {
		if (pgi->nInput & GIT_GROUP_MACRO) {
			if (_wcsicmp(szName, AtoW(pgi->Macro.szName)) == 0) {
				return i;
			}
		}
	}
	return ~0U;
}

// ---------------------------------------------------------------------------

static int gameInpAutoOne(GameInp* pgi, const char* szi)
{
	for (int i = 0; i < maxPlayers; i++) {
		int nSlide = playerDefaultControls[i] >> 4;
		switch (playerDefaultControls[i] & 0x0F) {
			case 0:										// Keyboard
				gamcAnalogKey(pgi, szi, i, nSlide);
				gamcPlayer(pgi, szi, i, -1);
				gamcMisc(pgi, szi, i);
				break;
			case 1:										// Joystick 1
				gamcAnalogJoy(pgi, szi, i, 0, nSlide);
				gamcPlayer(pgi, szi, i, 0);
				gamcMisc(pgi, szi, i);
				break;
			case 2:										// Joystick 2
				gamcAnalogJoy(pgi, szi, i, 1, nSlide);
				gamcPlayer(pgi, szi, i, 1);
				gamcMisc(pgi, szi, i);
				break;
			case 3:										// Joystick 3
				gamcAnalogJoy(pgi, szi, i, 2, nSlide);
				gamcPlayer(pgi, szi, i, 2);
				gamcMisc(pgi, szi, i);
				break;
			case 4:										// X-Arcade left side
				gamcMisc(pgi, szi, i);
				gamcPlayerHotRod(pgi, szi, i, 0x10, nSlide);
				break;
			case 5:										// X-Arcade right side
				gamcMisc(pgi, szi, i);
				gamcPlayerHotRod(pgi, szi, i, 0x11, nSlide);
				break;
			case 6:										// Hot Rod left side
				gamcMisc(pgi, szi, i);
				gamcPlayerHotRod(pgi, szi, i, 0x00, nSlide);
				break;
			case 7:										// Hot Rod right side
				gamcMisc(pgi, szi, i);
				gamcPlayerHotRod(pgi, szi, i, 0x01, nSlide);
				break;
			default:
				gamcMisc(pgi, szi, i);
		}
	}

	return 0;
}

static int addCustomMacro(wchar_t* value, bool overwrite)
{
	wchar_t* szQuote = NULL;
	wchar_t* szEnd = NULL;
	if (quoteRead(&szQuote, &szEnd, value)) {
		return 1;
	}

	int nMode = -1;
	int nInput = -1;
	bool createNew = false;
	struct BurnInputInfo bii;

	for (unsigned int j = gameInpCount; j < gameInpCount + macroCount; j++) {
		if (gameInput[j].nInput == GIT_MACRO_CUSTOM) {
			if (labelCheck(szQuote, AtoW(gameInput[j].Macro.szName))) {
				nInput = j;
				break;
			}
		}
	}

	if (nInput == -1) {
		if (macroCount + 1 == maxMacro) {
			return 1;
		}
		nInput = gameInpCount + macroCount;
		createNew = true;
	}

	wcscpy(szQuote, AtoW(gameInput[nInput].Macro.szName));

	if ((value = labelCheck(szEnd, L"undefined")) != NULL) {
		nMode = 0;
	} else {
		if ((value = labelCheck(szEnd, L"switch")) != NULL) {

			if (overwrite || gameInput[nInput].Macro.nMode == 0) {
				gameInput[nInput].Macro.Switch.nCode = (unsigned short)wcstol(value, &value, 0);
			}

			nMode = 1;
		}
	}

	if (nMode >= 0) {
		int nFound = 0;

		for (int i = 0; i < 4; i++) {
			gameInput[nInput].Macro.pVal[i] = NULL;
			gameInput[nInput].Macro.nVal[i] = 0;
			gameInput[nInput].Macro.nInput[i] = 0;

			if (value == NULL) {
				break;
			}

			if (quoteRead(&szQuote, &szEnd, value)) {
				break;
			}

			for (unsigned int j = 0; j < gameInpCount; j++) {
				bii.szName = NULL;
				BurnDrvGetInputInfo(&bii, j);
				if (bii.pVal == NULL) {
					continue;
				}

				wchar_t* szString = labelCheck(szQuote, AtoW(bii.szName));
				if (szString && szEnd) {
					gameInput[nInput].Macro.pVal[i] = bii.pVal;
					gameInput[nInput].Macro.nInput[i] = j;
					gameInput[nInput].Macro.nVal[i] = (unsigned char)wcstol(szEnd, &value, 0);

					nFound++;
					break;
				}
			}
		}

		if (nFound) {
			if (gameInput[nInput].Macro.pVal[nFound - 1]) {
				gameInput[nInput].nInput = GIT_MACRO_CUSTOM;
				gameInput[nInput].Macro.nMode = nMode;
				if (createNew) {
					macroCount++;
				}
				return 0;
			}
		}
	}

	return 1;
}

int gameInputAutoIni(int nPlayer, wchar_t* lpszFile, bool overwrite)
{
	analogSpeed = 0x0100;

	FILE* h = _wfopen(lpszFile, L"rt");
	if (h == NULL) {
		return 1;
	}

	wchar_t strLine[1024];
	int nFileVersion = 0;
	unsigned int i;

	// Go through each line of the config file and process inputs
	while (fgetws(strLine, sizeof(strLine), h)) {
		wchar_t* value;
		size_t nLen = wcslen(strLine);

		// Get rid of the linefeed at the end
		if (strLine[nLen - 1] == 10) {
			strLine[nLen - 1] = 0;
			nLen--;
		}

		value = labelCheck(strLine, L"version");
		if (value) {
			nFileVersion = wcstol(value, NULL, 0);
		}
		value = labelCheck(strLine, L"analog");
		if (value) {
			analogSpeed = wcstol(value, NULL, 0);
		}

		if (configMinVersion <= nFileVersion && nFileVersion <= burnVersion) {
			value = labelCheck(strLine, L"input");
			if (value) {
				wchar_t* szQuote = NULL;
				wchar_t* szEnd = NULL;
				if (quoteRead(&szQuote, &szEnd, value)) {
					continue;
				}

				if ((szQuote[0] == 'p' || szQuote[0] == 'P') && szQuote[1] >= '1'
					&& szQuote[1] <= '0' + maxPlayers && szQuote[2] == ' ') {
					if (szQuote[1] != '1' + nPlayer) {
						continue;
					}
				} else {
					if (nPlayer != 0) {
						continue;
					}
				}

				// Find which input number this refers to
				i = InputInfoToNum(szQuote);
				if (i == ~0U) {
					i = InputNameToNum(szQuote);
					if (i == ~0U) {
						continue;
					}
				}

				if (gameInput[i].nInput == 0 || overwrite) {			// Undefined - assign mapping
					stringToInp(gameInput + i, szEnd);
				}
			}

			value = labelCheck(strLine, L"macro");
			if (value) {
				wchar_t* szQuote = NULL;
				wchar_t* szEnd = NULL;
				if (quoteRead(&szQuote, &szEnd, value)) {
					continue;
				}

				i = macroNameToNum(szQuote);
				if (i != ~0U) {
					i += gameInpCount;
					if (gameInput[i].Macro.nMode == 0 || overwrite) {	// Undefined - assign mapping
						stringToMacro(gameInput + i, szEnd);
					}
				}
			}

			value = labelCheck(strLine, L"custom");
			if (value) {
				addCustomMacro(value, overwrite);
			}
		}
	}

	fclose(h);

	return 0;
}

// Auto-configure any undefined inputs to defaults
int gameInpDefault()
{
	GameInp* pgi;
	BurnInputInfo bii;
	unsigned int i;

	for (int nPlayer = 0; nPlayer < maxPlayers; nPlayer++) {

		if ((playerDefaultControls[nPlayer] & 0x0F) != 0x0F) {
			continue;
		}

		gameInputAutoIni(nPlayer, playerDefaultIni[nPlayer], false);
	}

	// Fill all inputs still undefined
	for (i = 0, pgi = gameInput; i < gameInpCount; i++, pgi++) {
		if (pgi->nInput) {											// Already defined - leave it alone
			continue;
		}

		// Get the extra info about the input
		bii.szInfo = NULL;
		BurnDrvGetInputInfo(&bii, i);
		if (bii.pVal == NULL) {
			continue;
		}
		if (bii.szInfo == NULL) {
			bii.szInfo = "";
		}

		// Dip switches - set to constant
		if (bii.nType & BIT_GROUP_CONSTANT) {
			pgi->nInput = GIT_CONSTANT;
			continue;
		}

		gameInpAutoOne(pgi, bii.szInfo);
	}

	// Fill in macros still undefined
	for (i = 0; i < macroCount; i++, pgi++) {
		if (pgi->nInput != GIT_MACRO_AUTO || pgi->Macro.nMode) {	// Already defined - leave it alone
			continue;
		}

		gameInpAutoOne(pgi, pgi->Macro.szName);
	}

	return 0;
}

// ---------------------------------------------------------------------------
// Write all the GameInps out to config file 'h'

int gameInpWrite(FILE* h, bool writeConst)
{
	// Write input types
	for (unsigned int i = 0; i < gameInpCount; i++) {
		GameInp* pgi = gameInput + i;
		if (pgi->nInput == GIT_CONSTANT && !writeConst) {
			continue; // skip constant, added by regret
		}

		// save input info name instead, modified by regret
		wchar_t* szName = NULL;
		if (pgi->nInput == GIT_CONSTANT) {
			szName = InputNumToName(i);
		} else {
			szName = InputNumToInfo(i);//InputNumToName(i);
		}
		fwprintf(h, L"input  \"%s\" ", szName);

		int nPad = 16 - wcslen(szName);
		for (int j = 0; j < nPad; j++) {
			fwprintf(h, L" ");
		}

		fwprintf(h, L"%s\n", inpToString(gameInput + i));
	}

	fwprintf(h, L"\n");

	GameInp* pgi = gameInput + gameInpCount;
	for (unsigned int i = 0; i < macroCount; i++, pgi++) {
		int nPad = 0;

		if (pgi->nInput & GIT_GROUP_MACRO) {
			switch (pgi->nInput) {
				case GIT_MACRO_AUTO:									// Auto-assigned macros
					fwprintf(h, L"macro  \"%hs\" ", pgi->Macro.szName);
					break;
				case GIT_MACRO_CUSTOM:									// Custom macros
					fwprintf(h, L"custom \"%hs\" ", pgi->Macro.szName);
					break;
				default:												// Unknown -- ignore
					continue;
			}

			nPad = 16 - strlen(pgi->Macro.szName);
			for (int j = 0; j < nPad; j++) {
				fwprintf(h, L" ");
			}
			fwprintf(h, L"%s\n", inpMacroToString(pgi));
		}
	}

	return 0;
}

// ---------------------------------------------------------------------------

// Read a GameInp in
int gameInpRead(wchar_t* value, bool overwrite)
{
	wchar_t* szQuote = NULL;
	wchar_t* szEnd = NULL;

	int ret = quoteRead(&szQuote, &szEnd, value);
	if (ret) {
		return 1;
	}

	// Find which input number this refers to
	unsigned int i = InputInfoToNum(szQuote);
	if (i == ~0U) {
		i = InputNameToNum(szQuote);
		if (i == ~0U) {
			return 1;
		}
	}

	if (overwrite || gameInput[i].nInput == 0) {
		// Parse the input description into the GameInp structure
		stringToInp(gameInput + i, szEnd);
	}

	return 0;
}

int gameInpMacroRead(wchar_t* value, bool overwrite)
{
	wchar_t* szQuote = NULL;
	wchar_t* szEnd = NULL;

	int ret = quoteRead(&szQuote, &szEnd, value);
	if (ret) {
		return 1;
	}

	unsigned int i = macroNameToNum(szQuote);
	if (i != ~0U) {
		i += gameInpCount;
		if (gameInput[i].Macro.nMode == 0 || overwrite) {
			stringToMacro(gameInput + i, szEnd);
		}
	}

	return 0;
}

int gameInpCustomRead(wchar_t* value, bool overwrite)
{
	return addCustomMacro(value, overwrite);
}
